; -------------------------------------------------------------------------------------------------------------------------
        ; boot-loader程序
; -------------------------------------------------------------------------------------------------------------------------
%include "boot.inc"
LOADER_STACK_TOP equ LOADER_BASE_ADDR
        
section loader vstart=LOADER_BASE_ADDR
	
	jmp loader_start                                    ; 下面是一大堆数据段，无法执行，跳转到loader程序入口
	
	; ---------- GDT及其内部描述符 ----------
GDT_BASE:                                                   ; 第0个描述符不可用！
	dd 0x00000000
       	dd 0x00000000
        
CODE_DESC:                                          
       	dd 0x0000FFFF
        dd DESC_CODE_HIGH4
        
DATA_STACK_DESC:   
        dd 0x0000FFFF
        dd DESC_DATA_HIGH4
        	
VIDEO_DESC:
        dd 0x80000007                                       ; 显存的0xb0000~0xb7fff为文本模式显示区，(0xbffff-0xb8000+1)/4K-1=0x7
        dd DESC_VIDEO_HIGH4
        
        GDT_SIZE   equ $-GDT_BASE                           ; 当前GDT大小
        GDT_LIMIT  equ GDT_SIZE-1                           ; 通过GDT大小求出GDT界限，方便下文定义gdt_ptr以供lgdt使用
        times 60 dq 0                                       ; 预留 60 个描述符的空位（dq = define quad-word，定义2个dword）
        
        ; ---------- 第1～3个描述符的段选择子 ----------
        SELECTOR_CODE  equ (0x0001<<3) + TI_GDT + RPL0      
        SELECTOR_DATA  equ (0x0002<<3) + TI_GDT + RPL0       
        SELECTOR_VIDEO equ (0x0003<<3) + TI_GDT + RPL0        
        
        ; ---------- GDT的指针 ----------
        gdt_ptr:
        	dw GDT_LIMIT                                ; 前2字节为GDT界限
        	dd GDT_BASE                                 ; 后4字节是GDT起始地址
        
        ; ---------- 底部消息字符串 ----------
        realmodemsg    db 'system in real mode.'
        protectmodemsg db 'system in protect mode.'

; ------------------------------------------------------------------------------------------------------------------------------
	; loader程序正式开始
; ------------------------------------------------------------------------------------------------------------------------------
loader_start:        
        mov sp, LOADER_BASE_ADDR                            ; 进入保护模式后要使用esp，只是单纯为esp作初始化(写成mov dw esp,ADR也行)
        
        ; ---------- 底部打印实模式信息 ----------
        mov bp, realmodemsg
        mov cx, 20
        
        call print_msg
                
        
; ------------------------------------------------------------------------------------------------------------------------------
	; 准备进入保护模式
; ------------------------------------------------------------------------------------------------------------------------------
transfer_protect_mode:
	; ---------- 打开 A20 ----------
	in al, 0x92                                          
	or al, 00000010B                                    
	out 0x92, al
	
	; ---------- 加载GDT ----------
	lgdt [gdt_ptr]

	; ---------- cr0第0位置1 ----------
	mov eax, cr0
	or eax, 0x00000001
	mov cr0, eax
	
	jmp dword SELECTOR_CODE: p_mode_start
        

; ------------------------------------------------------------------------------------------------------------------------------
	; 在界面底部打印字符串，展示系统当前的模式
	; ---------------------------------------
	; 输入：
	; bp=输入字符串的地址；
	; cx=字符串长度;
; ------------------------------------------------------------------------------------------------------------------------------
print_msg:

        ; ------------------------------------------------------------
	; INT 0x10    子功能号: 0x13    子功能描述: BIOS中断，打印字符串
	; ------------------------------------------------------------
        ; 输入：
        ; AH=子功能号;  AL=显示输出方式                      
        ; BH=页码;      Bl=字符属性
        ; CX=字符串长度
        ; (DH, DL)=坐标（行，列）
        ; ES:BP=字符串地址
                
        mov ax, 0x1301
        mov bx, 0x001f
        mov dx, 0x1800
        int 0x10
        
        ret


; ------------------------------------------------------------------------------------------------------------------------------
	; 已经处于保护模式
; ------------------------------------------------------------------------------------------------------------------------------

[bits 32]
p_mode_start:

	; 添加如下代码会异常执行
	; 因为print_msg函数的编码规则是bits 16，因此mov ax, 0x1301指令一直是B80113
	; 而当前是保护模式（bits 32），想要mov ax, 0x1301指令正确执行需要添加前缀0x66，而这是做不到的
	; 也许你会说，给print_msg前添加[bits 32]即可，那么在实模式时调用它便会出错
	
	; mov bp, protectmodemsg
	; mov cx, 23
	; call print_msg

	mov esp, LOADER_STACK_TOP
	mov ax, SELECTOR_DATA
	mov ds, ax
	mov es, ax
	mov ss, ax
	mov ax, SELECTOR_VIDEO
	mov gs, ax
	
	mov byte [gs:160], 'P'
	
	jmp $
